package com.qj.tool;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author dlw
 * @date 2025-06-03 16:42
 * @desc
 * 这个 HttpUtils 类的设计和使用主要是为了在后端发起 HTTP 请求到其他服务或 API。
 * 在一些后端服务或微服务架构中，服务之间可能需要通信或调用外部系统的 API。
 * 这种情况下，后端本身就需要能够发送 HTTP 请求，包括 POST 请求。
 * 以下是一些常见的使用场景和 HttpUtils 类的作用：
 * 1. 后端服务间通信
 * 在微服务架构中，不同的服务通常通过 HTTP 接口进行通信。例如：
 * ● 用户服务可能需要从订单服务获取数据。
 * ● 库存服务可能需要将信息发送到物流服务。
 * 在这种情况下，后端服务需要自己发起请求，而不仅仅是等待前端发来的请求。
 * 2. 第三方 API 调用
 * 后端服务常常需要调用第三方的 API。例如：
 * ● 调用支付服务、短信网关或邮件服务等。
 * ● 与第三方服务对接，完成业务逻辑。
 * 这些调用通常需要后端直接发送 HTTP 请求。
 * 3. 为什么创建 HttpUtils 工具类
 * ● 复用性：提供通用的 post 请求方法，避免在每个需要发起请求的地方重复写 HTTP 客户端代码。
 * ● 简化 HTTP 客户端配置：初始化了连接池、请求超时、连接超时等配置参数，确保在高并发下性能和资源使用得到有效管理。
 * ● 易于维护：将 HTTP 请求的封装代码集中在一个类中，如果需要更改请求逻辑或参数设置，可以统一管理，不需要修改每个调用 HTTP 请求的地方。
 * 总结
 * 这个 HttpUtils 工具类可以帮助后端服务在特定场景下发起 HTTP POST 请求，从而实现微服务间通信、调用第三方 API 和提高代码复用性。
 * 在这些场景下，后端不仅是处理前端传入的请求，还需要主动调用其他系统的服务，因此需要有一个可靠的工具类来管理和发送 HTTP 请求。
 */
@Slf4j
public class HttpUtils {
    static CloseableHttpClient httpClient;

    static {
        // 注册http和https的相关内容
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();

        // 创建连接池并设置相关属性
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        connectionManager.setMaxTotal(500);
        connectionManager.setDefaultMaxPerRoute(500);
        connectionManager.setDefaultSocketConfig(
                SocketConfig.custom().setSoTimeout(15, TimeUnit.SECONDS)
                        .setTcpNoDelay(true).build()
        );
        connectionManager.setValidateAfterInactivity(TimeValue.ofSeconds(15));

        // 配置RequestConfig
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(Timeout.ofSeconds(1))
                .setConnectionRequestTimeout(Timeout.ofSeconds(1))
                .setResponseTimeout(Timeout.ofSeconds(1))
                .build();

        // 创建HttpClients
        httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(requestConfig)
                .disableAutomaticRetries()
                .build();
    }

    // 自定义封装方法(post方法)
    public static String post(String url, List<Pair<String, String>> pairList, Map<String, String> headerMap) throws Exception {
        String urlParam = buildUrlParam(pairList);
        url = url + "?" + urlParam;
        final HttpPost httpPost = new HttpPost(url);
        if (Objects.nonNull(headerMap) && headerMap.size() > 0) {
            headerMap.forEach((key, value) -> httpPost.addHeader(key, value));
        }
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            return EntityUtils.toString(response.getEntity());
        } finally {
            if (null != response) {
                EntityUtils.consume(response.getEntity());
            }
        }
    }

    // 将 键值对 转换为 name=qj&age=18 的形式
    private static String buildUrlParam(List<Pair<String, String>> pairList) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Pair<String, String> dataPair : pairList) {
            stringBuilder.append(dataPair.getKey()).append("=").append(dataPair.getValue()).append("&");
        }
        stringBuilder.setLength(stringBuilder.length() - 1);
        return stringBuilder.toString();
    }
}
